using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.IO;
using System.Net; 

namespace Rest
{
    /// <summary>
    /// Represents an asynchronous Execute operation
    /// </summary>
    /// <remarks>
    /// See: http://msdn.microsoft.com/msdnmag/issues/07/03/ConcurrentAffairs/default.aspx?loc=&fig=true#fig3
    /// </remarks>
    public class ExecuteAsyncResult : IAsyncResult
    {
        #region Properties

        private readonly Object asyncState;
        private ManualResetEvent asyncWaitHandle;
        private readonly AsyncCallback callback;
        private Int32 currentState = StatePending;
        private Exception exception;
        private HttpWebResponse response;

        private const Int32 StatePending = 0;
        private const Int32 StateCompletedAsynchronously = 1;
        private const Int32 StateCompletedSynchronously = 2;
        
        /// <summary>
        /// The response returned from the execution
        /// </summary>
        public HttpWebResponse Response
        {
            get { return response; }
            set { response = value; }
        }
        
        #endregion

        /// <summary>
        /// ctor : AsyncCallback, Object
        /// </summary>
        /// <param name="callback"></param>
        /// <param name="asyncState"></param>
        public ExecuteAsyncResult(AsyncCallback callback, Object asyncState) 
        {
            this.callback = callback;
            this.asyncState = asyncState; 
        }

        /// <summary>
        /// Sets the operation as completed
        /// </summary>
        public void SetAsCompleted(
            HttpWebResponse response, 
            Exception exception,
            Boolean completedSynchronously)
        {
            if (response != null)
            {
                response.Close();  
            }

            this.response = response; 
            
            this.exception = exception;

            //
            // Set state to complete
            //
            Int32 prevState = Interlocked.Exchange(ref currentState,
                completedSynchronously ? StateCompletedSynchronously :
                StateCompletedAsynchronously);

            if (prevState != StatePending)
            {
                throw new InvalidOperationException(
                    "Result can only be set once");  
            }

            //
            // Notify waiting threads
            //
            if (asyncWaitHandle != null)
            {
                asyncWaitHandle.Set();  
            }

            //
            // Invoke the callback
            //
            if (callback != null) 
            {
                callback(this); 
            }
        }

        /// <summary>
        /// Completes pending asynchronous operation by blocking on
        /// AsyncWaitHandle.
        /// </summary>
        public void EndInvoke() 
        {
            if (!IsCompleted)
            {
                AsyncWaitHandle.WaitOne();
                AsyncWaitHandle.Close();
                asyncWaitHandle = null; 
            }

            if (exception != null)
            {
                throw exception;
            }
        }

        #region IAsyncResult Members

        /// <summary>
        /// 
        /// </summary>
        public Object AsyncState
        {
            get { return asyncState; }
        }

        /// <summary>
        /// A wait handle 
        /// </summary>
        public WaitHandle AsyncWaitHandle
        {
            get
            {
                if (null == asyncWaitHandle)
                {
                    Boolean isComplete = IsCompleted;

                    if (null == Interlocked.CompareExchange(
                        ref asyncWaitHandle,
                        new ManualResetEvent(isComplete),
                        null))
                    {
                        // Signal to waiting threads if complete
                        if (!isComplete && IsCompleted)
                        {
                            asyncWaitHandle.Set();
                        }
                    }
                }

                return asyncWaitHandle;
            }
        }

        /// <summary>
        /// Indicates the operation completed synchronously
        /// </summary>
        public bool CompletedSynchronously
        {
            get
            {
                return Thread.VolatileRead(ref currentState) ==
                    StateCompletedSynchronously;
            }
        }

        /// <summary>
        /// Indicates the operation has completed
        /// </summary>
        public bool IsCompleted
        {
            get
            {
                return Thread.VolatileRead(ref currentState) != StatePending;
            }
        }

        #endregion
    }
}
